home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr10 / swagg_m.zip / MEMORY.SWG < prev    next >
Text File  |  1993-06-11  |  64KB  |  1 lines

  1. SWAGOLX.EXE (c) 1993 GDSOFT  ALL RIGHTS RESERVED 00014         MEMORY/DPMI MANAGEMENT ROUTINES                                   1      05-28-9313:50ALL                      SWAG SUPPORT TEAM        BIGMEM1.PAS              IMPORT              13          > I have seen posts about using Pointer Arrays instead of the standard fixedπ> Arrays.  These posts have been helpful but I think the rewriting of an exampleπ> problem would benefit me the best.  Please take a look at this simple exampleπ> code:π>π> Program LotsofData;π>π> Type LOData = Array [1..10000] of Real;π>π> Var Value : LOData;π>     MaxSizeArray, I, NumElement : Integer;π>     NewValue : Real;π>π> beginπ>   Write('Please input the Maximum Size of the Array: ');π>   Readln(MaxSizeArray);π>   For I := 1 to MaxSizeArray Doπ>     Value[I] := 0.0;π>   Writeln('Array Initialized');π>   Writeln;π>   Write('Please input the Number of Array Element to Change: ');π>   Readln(NumElement);π>   Write('Please input the New Number For Value[',NumElement,']: ');π>   Readln(NewValue);π>   Value[NumElement] := NewValue;π> end.π>ππResponse;π1. Declare the Variable Value as LOData -π    e.g. Var Value : LOData;ππ2. Read MaxSizeArray;ππ3. Allocate memory For the Array by using NEW() or GETMEM()π     e.g. NEW(Value);π    or   GetMem(Value, Sizeof(Real) * MaxSizeArray);ππGetmem() is better because you can allocate just the precise amount ofπmemory needed.ππ4. From then on refer to your Array as Valueπ    e.g. Value[Element] := NewValue;ππ5. When you finish, deallocate memory byπ    [a] Dispose(Value) - if you used NEW() to begin with, orπ    [b] FreeMem(Value, Sizeof(Real) * MaxSizeArray) - if you usedπ    GetMem() to begin with.ππ                                                                                                                    2      05-28-9313:50ALL                      SWAG SUPPORT TEAM        BIGMEM2.PAS              IMPORT              10          {πBP7 is not limited to 16Meg of memory, by running the Program below in aπWindows 3.1 Window, it created 744 Objects allocating 30Meg of memory. Theπfinal printout verified that all the items were still there.ππSo if you use a third party DPMI server, you should be able to use all yourπmemory.ππI might point out that I allocated 30Meg of memory on my 16Meg machine. I runπWindows 3.1 With a 32Meg permanent swap File.π}ππProgram BigMemory;πUsesπ OpStrDev,Objects;ππTypeπ PDataType=^DataType;π DataType=Object(tObject)π   C:LongInt;π   S:String;π   Stuffing:Array[1..40000] of Byte;π   Constructor Init(I:LongInt);π end;πVarπ Counter:LongInt;π List:TCollection;ππConstructor DataType.Init(I:LongInt);πbeginπ tObject.Init;π C:=I;π Write(tpstr,'I = ',I,' I div 2 =',I div 2);π S:=returnstr;πend;ππProcedure Printall;π Procedure PrintOne(P:PDataType);Far;π beginπ   Writeln(P^.C,' - ',P^.S);π end;πbeginπ List.Foreach(@PrintOne);πend;ππbeginπ Counter:=0;π List.Init(1000,1000);π Repeatπ   inc(Counter);π   List.Insert(New(PDataType,Init(Counter)));π   Write(Counter,' mem =',Memavail,^M);π Until Memavail<50000;π PrintAll;πend.π                         3      05-28-9313:50ALL                      SWAG SUPPORT TEAM        CMOSSTUF.PAS             IMPORT              69          (*******************************************************************)π Program SaveCMOS;              { Compiler: Turbo & Quick Pascal    }π{                                                                   }π{ File name: SaveCMOS.PAS       coded: Mar.3.1993, Greg Vigneault   }π{                                                                   }π{ This utility will read the entire contents of the CMOS RAM, and   }π{ save it to a File.  Invoke this Program as...                     }π{                                                                   }π{               SAVECMOS <Filename>                                 }π{                                                                   }π Uses   Crt;                        { import ReadKey                }π Const  AddressRTC  = $70;          { RTC register address latch    }π        DataRTC     = $71;          { RTC register data             }π        AStatusRTC  = $0A;          { RTC status register A         }π Var    tempCMOS,π        RegCMOS     : Byte;                     { RTC register      }π        MapCMOS     : Array [0..63] of Byte;    { RTC CMOS reg map  }π        OutFile     : File;                     { saved CMOS data   }π        ch          : Char;                     { For user input    }π        FResult     : Integer;                  { check File Write  }π(*-----------------------------------------------------------------*)π Function ReadCMOS( RegCMOS :Byte ) :Byte;π    beginπ        RegCMOS := RegCMOS and $3F;     { don't set the NMI bit     }π        if (RegCMOS < AStatusRTC) then  { wait For end of update?   }π            Repeatπ                Port[AddressRTC] := AStatusRTC;     { read status   }π            Until (Port[DataRTC] and $80) <> 0;     { busy bit      }π        Port[AddressRTC] := RegCMOS;    { tell RTC which register   }π        ReadCMOS := Port[DataRTC];      { and read in the data Byte }π    end {ReadCMOS};π(*-----------------------------------------------------------------*)π Procedure HelpExit;π    begin   WriteLn; WriteLn( 'Usage: SAVECMOS <Filename>' );π            WriteLn( CHR(7) );  Halt(1);π    end {HelpExit};π(*-----------------------------------------------------------------*)π beginπ    WriteLn; WriteLn( 'SaveCMOS v0.1  Greg Vigneault' ); WriteLn;π    if (ParamCount <> 1) then HelpExit;π    Assign( OutFile, ParamStr(1) );π    {$i-}  Reset( OutFile, SizeOf(MapCMOS) );  {$i+}π    if (IoResult = 0) then beginπ        Repeatπ          Write('File ',ParamStr(1),' exists! OverWrite? (Y/N): ',#7);π          ch := UpCase( ReadKey );  WriteLn;π        Until (ch in ['Y','N']);π        if (ch = 'N') then begin WriteLn('ABORTED'); Halt(2); end;π    end;π    ReWrite( OutFile, SizeOf(MapCMOS) );  WriteLn;π    For RegCMOS := 0 to 63 do MapCMOS[RegCMOS] := ReadCMOS(RegCMOS);π    MapCMOS[AStatusRTC] := MapCMOS[AStatusRTC] and $7F; { clear UIP }π    BlockWrite( OutFile, MapCMOS, 1, FResult );π    if (FResult <> 1) then beginπ        WriteLn( 'Error writing to ',ParamStr(1),'!',#7 );π        Close( OutFile );   Halt(3);π    end;π    FillChar( MapCMOS, SizeOf(MapCMOS), 0 );π    Reset( OutFile, SizeOf(MapCMOS) );π    BlockRead( OutFile, MapCMOS, 1, FResult );π    if (FResult <> 1) then beginπ        WriteLn( 'Error reading from ',ParamStr(1),'!',#7 );π        Close( OutFile );   Halt(4);π    end;π    Close(OutFile);π    For RegCMOS := 10 to 63 do begin { don't include time in verify }π        if (RegCMOS = AStatusRTC) thenπ            MapCMOS[RegCMOS] := MapCMOS[RegCMOS] and $7F;π        if (MapCMOS[RegCMOS] <> ReadCMOS(RegCMOS)) then beginπ            WriteLn('!!! Error: can''t verify File contents !!!');π            WriteLn(#7#7#7#7#7); Halt(5);π        end;π    end;π    WriteLn('! The CMOS RAM has now been saved in ',ParamStr(1),#7);π end {SaveCMOS}.π(*******************************************************************)ππ Greg_ππ Mar.03.1993.Toronto.Canada.         greg.vigneault@bville.gts.orgπ---π ■ QNet3ß ■ City2City / 1/0/11 / Baudeville BBS / Toronto / 416-283-0114π<<<>>>πππDate: 03-04-93 (03:03)              Number: 127 of 160 (Echo)π  To: CHRIS LAUTENBACH              Refer#: NONEπFrom: GREG VIGNEAULT                  Read: 03-05-93 (17:02)πSubj: TP: LOADCMOS SOURCE CODE      Status: PUBLIC MESSAGEπConf: C-ProgramMING (368)        Read Type: GENERAL (+)ππ(*******************************************************************)π Program LoadCMOS;              { Compiler: Turbo & Quick Pascal    }π{                                                                   }π{ File name: LoadCMOS.PAS       coded: Mar.3.1993, Greg Vigneault   }π{                                                                   }π{               LOADCMOS <Filename>                                 }π{                                                                   }π Uses   Crt;                        { import ReadKey                }π Const  AddressRTC      = $70;      { RTC register address latch    }π        DataRTC         = $71;      { RTC register data             }π        AStatusRTC      = $0A;      { RTC status register A         }π        BStatusRTC      = $0B;      { RTC status register B         }π        CStatusRTC      = $0C;      { RTC status register C         }π        DStatusRTC      = $0D;      { RTC status register D         }π        SecondsRTC      = 0;        { seconds       (BCD, 0..59)    }π        MinutesRTC      = 2;        { minutes       (BCD, 0..59)    }π        HoursRTC        = 4;        { hours         (BCD, 0..23)    }π        WeekDayRTC      = 6;        { day of week   (1..7)          }π        DayOfMonthRTC   = 7;        { day of month  (BCD, 1..31)    }π        MonthRTC        = 8;        { month         (BCD, 1..12)    }π        YearRTC         = 9;        { year          (BCD, 0..99)    }π Var    RegCMOS     : Byte;                     { RTC register      }π        MapCMOS     : Array [0..63] of Byte;    { RTC CMOS reg map  }π        ChkSumCMOS  : Integer;                  { CMOS checksum     }π        InFile      : File;                     { saved CMOS data   }π        ch          : Char;                     { For user input    }π        FResult     : Integer;                  { check File Write  }π(*-----------------------------------------------------------------*)π Procedure WriteCMOS( RegCMOS, Value :Byte );π    Var temp : Byte;π    beginπ        if not (RegCMOS in [0,1,CStatusRTC,DStatusRTC]) thenπ        beginπ            if (RegCMOS < CStatusRTC) then beginπ                Port[AddressRTC] := BStatusRTC;π                temp := Port[DataRTC] or $80;       { stop the clock}π                Port[AddressRTC] := BStatusRTC;π                Port[DataRTC] := temp;π            end;π            Port[AddressRTC] := RegCMOS and $3F;    { select reg    }π            Port[DataRTC] := Value;                 { Write data    }π            if (RegCMOS < CStatusRTC) then beginπ                Port[AddressRTC] := BStatusRTC;π                temp := Port[DataRTC] and not $80;  { enable clock  }π                Port[AddressRTC] := BStatusRTC;π                Port[DataRTC] := temp;π            end;π        end;π    end {WriteCMOS};π(*-----------------------------------------------------------------*)π Procedure HelpExit;π    begin   WriteLn; WriteLn( 'Usage: LOADCMOS <Filename>' );π            WriteLn( CHR(7) );  Halt(1);π    end {HelpExit};π(*-----------------------------------------------------------------*)π beginπ    WriteLn; WriteLn( 'LoadCMOS v0.1  Greg Vigneault' ); WriteLn;π    if (ParamCount <> 1) then HelpExit;π    Assign( InFile, ParamStr(1) );π    {$i-}  Reset( InFile, SizeOf(MapCMOS) );  {$i+}π    if (IoResult <> 0) then beginπ          Write('Can''t find ',ParamStr(1),'!',#7);π          Halt(1);π    end;π    FillChar( MapCMOS, SizeOf(MapCMOS), 0 );        { initialize    }π    BlockRead( InFile, MapCMOS, 1, FResult );       { saved CMOS    }π    Close(InFile);π    if (FResult <> 1) then beginπ        WriteLn('! Error reading File',#7);π        Halt(2);π    end;π    MapCMOS[AStatusRTC] := MapCMOS[AStatusRTC] and $7F;π    ChkSumCMOS := 0;                                { do checksum   }π    For RegCMOS := $10 to $2Dπ        do ChkSumCMOS := ChkSumCMOS + Integer( MapCMOS[RegCMOS] );π    if (Hi(ChkSumCMOS) <> MapCMOS[$2E])π    or (Lo(ChkSumCMOS) <> MapCMOS[$2F]) then beginπ        WriteLn('!!! CheckSum error in ',ParamStr(1) );π        WriteLn(#7#7#7#7#7);  Halt(2);π    end;π    For RegCMOS := AStatusRTC to 63π        do WriteCMOS( RegCMOS, MapCMOS[RegCMOS] );π    WriteLn('! The CMOS RAM has been restored from ',ParamStr(1),#7);π end {LoadCMOS}.π(*******************************************************************)π                                4      05-28-9313:50ALL                      SWAG SUPPORT TEAM        DPMIINFO.PAS             IMPORT              164         Hello All,ππAgain, interrupts from protected mode. This is an updated version of myπprevious article, which, by the way, generated much less respons (none)πthan I expected. Where are the BTrieve Programmers, the DesqView APIπWriters, the fossil Writers, the .... Maybe they know everythingπalready. Well then, what has been changed?ππ* little bugs fixed (memory not freed, SEG does not work, etc.)π* I stated that if you want to pass parameters on the stack you had toπ  do low level stuff. This is not necessary. I do everything in highπ  level(?) pascal now.π* Point 5 of the first Type of unsupported interrupts was inComplete.π  There's sometimes much more work involved :-(π* A simple Unit is presented, which helps to cut down code size. Seeπ  Appendix AππCompiling Real to protected mode has been very simple For most of us.πJust Compile and go ahead. 99.5% of your code works fine. But the otherπ0.5% is going to give you some hard, hard work.π   In this article I describe first how I first stuck on the protectedπstone. Than I try to give a general overview of problems one mightπencounter when using interrupts. Next I describe the solutions or giveπat least some hints, and I give a solution to the original Program whichπmade me aware of protected mode conversion problems. Appendix A listsπthe code For a Unit I found usefull when porting my DesqView API toπprotected mode.π    References can be found at the end of this article. of course, allπdisclaimers you can come up With apply!πππWhen Compiling a big Program, which supported DesqView, a GP faultπoccurred. It was simple to trace the bug down: TDX would show me theπoffending code. You can get the same error if you try to run theπfollowing Program in protected mode:ππ========cut here==========================πProgram Test;ππFunction  dv_win_me : LongInt;  Assembler;πAsmπ  mov    bx,0001hπ  mov    ah,12hπ  int    15h       {* push dWord handle on stack *}π  pop    ax        {* pop it *}π  pop    dx        {* and return it *}πend;ππbeginπ  Writeln(dv_win_me);πend.π========cut here==========================ππThis little Program must be run under DesqView. When run under DesqViewπit returns the current Window handle on the stack. BUT: when Compiledπunder protected mode NO dWord handle is returned on the stack. So aπstack fault occurs.ππWhat happened? I stuck on one of those unsupported interrupts. Onlyπsupported interupts guarantee to return correct results. You can find aπlist of all supported interrupts in the Borland Open ArchitectureπHandboek For Pascal, Chapter 2 (seperate sold by Borland, not includedπin your BP7 package). Supported are the general Dos and Bios interrupts.ππBeFore eleborating on supported and unsupported interrupts, I have toπexplain a few issues which are probably new to us Pascal Programmers.πWhenever a user interrupt occurs in protected mode (you issue a int xxπcall) Borlands DPMI Extender switches to Real mode, issues theπinterrupt, and switches back to protected mode.ππThis works find For most Cases: interrupts which only pass registerπparameters work fine. But what happens if you, For example, called theπPrint String Function? (int 21h, ah=09h). You pass as parameters ds:dxπpointing to the String to be printed. But, be aware: in protected modeπds contains not a segment but a selector! and the selector in dsπprobably points to an area above the 1MB boundary. These two things areπgoing to give Real mode Dos big, big problems. Don't even try it!π    So Borland's DPMI Extender does more than just switching fromπprotected to Real mode when an interrupt occurs: it translates selectorsπto segments when appropriate. But, it can only do so For interrupts itπKNOWS that they need a translation. Such interrupts are calledπsupported. Interrupts about which Borland's DPMI Extender does not knowπabout are unsupported. and they are going to give you Real problems!ππSo you see, when only data is passed in Registers, everything worksπfine. But if you need to pass Pointers, there is a problem. But why didπthe above Program not work? It didn't use selectors you might ask. Well,πthere is another set of interrupts that are unsupported: those thatπexpect or return values on the stack. This is the Case With the aboveπProgram.ππSo, to conclude:π* supported interruptsπ  - simple parameter passing using Registers, no segments/selectorsπ    or stacks includedπ  - interrupts which Borland's DPMI Extender knows about (too few Forπ    most of us)π* unsupported interruptsπ  - using segments/selectorsπ  - involving stacksππIn the next two sections I will fix both Types of problems. I make useπof the DPMI Unit, which comes With the Open Architecture Handbook. Youπdo not need this Unit. As this DPMI Unit is just a wrapper around theπDPMI interrupt 31h, simply looking the interrupts up in Ralph Brown'sπinterrupts list and writing Functions/Procedures For them, works fine.πππUnsupported interrupts which need segmentsπ------------------------------------------ππBecause the data segment and stack segment reside in protected mode, youπneed to allocate memory in Real mode, copy your data (which residesπabove 1MB) and issue the interrupt by calling the DPMI Simulate RealπInterrupt. So our to-do list is:π1) allocate Real mode memoryπ2) copy data from protected mode to Real modeπ3) set up the Real mode Registersπ4) issue interruptπ5) examine resultsππ1) You can allocate Real mode memory by issueing a GlobalDosAlloc (notπ   referenced in the online help, but you can look it up in theπ   Programmer's refercence manual) request. The GlobalDosAlloc is in theπ   WinApi Unit. For example:ππ     Uses WinAPI;π     Varπ       Return : LongInt;π       MemSize : LongInt;π     beginπ       MemSize := 1024;π       Return := GlobalDosAlloc(MemSize);π     end;ππ   This call allocates a block of memory, 1K in size, below the 1MBπ   boundary. The value in Return should be split in LongRec(Return).Loπ   and LongRec(Return).Hi. The Hi-order Word contains the segment baseπ   address of the block. The low-order Word contains the selector Forπ   the block.ππ2) You use the selector to acces the block from protected mode and youπ   use the segment of the block to acces the block within Real mode (yourπ   interrupt).π       For example: we want to exchange messages With some interrupt. Theπ   code For this would be:π     Uses WinAPI;π     Varπ       Return : LongInt;π       MemSize : LongInt;π       RealModeSel : Pointer;π       RealModeSeg : Pointer;π       Message : String;π     beginπ       MemSize := 256;π       Return := GlobalDosAlloc(MemSize);π       PtrRec(RealModeSel).seg := LongRec(Return).Lo;π       PtrRec(RealModeSel).ofs := 0;π       PtrRec(RealModeSeg).seg := LongRec(Return).Hi;π       PtrRec(RealModeSeg).ofs := 0;ππ     {* Both RealModeSel(ector) and RealModeSeg(ment) point to the sameππ        physical address now. *}ππ     {* move message from protected mode memory to the allocated selector *}π       Message := 'How are your?';π       Move(Message, RealModeSel^, Sizeof(Message));ππ     {* issue interupt, explained below *}π     { <..code..> }π     {* the interrupt returns a message *}ππ     {* move interrupt's message below 1MB to protected mode *}π       Move(RealModeSel^, Message, Sizeof(Message));π       Writeln(Message);     {* "yes, I'm fine. Thank you!" *}π     end;ππ3) We will now examine how to setup an interrupt For Real mode. Most ofπ   the time this is transparantly done by Borland's DPMI Extender, butπ   we are on our own now. to interrupt Dos, we use the DPMI Functionπ   31h, 0300h. This interrupt simulates an interrupt in Real mode.ππ   The Simulate Real Mode Interrupt Function needs a Real mode registerπ   data structure. We pass the interrupt and the Real mode register dataπ   structure to this Function, which will than start to simulate theπ   interrupt.π       This Function switches to Real mode, copies the contents of theπ   data structure into the Registers, makes the interrupt, copies theπ   Registers back into the supplied data structure, switches theπ   processor back to protected mode and returns. Voila: you are inπ   control again.π       Maybe you ask: why need I to setup such a data structure? Why canπ   I not simply pass Registers? Several reasons exist, but take Forπ   example the RealModeSeg of the previous example. You cannot simplyπ   load a RealModeSeg in a register. Most likely a segment violationπ   would occur (referring to a non existing segment or you do not haveπ   enough rights etc.). ThereFore only in Real mode can Real modeπ   segments be loaded.ππ       The data structure to pass Registers between protected and Realπ   mode can be found in the DPMI Unit which I Repeat here:ππ     Typeπ       TRealModeRegs = Recordπ         Case Integer ofπ           0: (π               EDI, ESI, EBP, EXX, EBX, EDX, ECX, EAX: LongInt;π               Flags, ES, DS, FS, GS, IP, CS, SP, SS: Word);π           1: (π               DI,DIH, SI, SIH, BP, BPH, XX, XXH: Word;π               Case Integer ofπ                 0: (π                     BX, BXH, DX, DXH, CX, CXH, AX, AXH: Word);ππ                 1: (π                     BL, BH, BLH, BHH, DL, DH, DLH, DHH,π                     CL, CH, CLH, CHH, AL, AH, ALH, AHH: Byte));π         end;ππ   This looks reasonably Complex, doesn't it! More simply is theπ   following structure (found in, For example, "Extending Dos" by Rayπ   Duncan e.a.)π   offset  Lenght Contentsπ   00h     4      DI or EDIπ   04h     4      SI or ESIπ   08h     4      BP or EBPπ   0Ch     4      reserved, should be zeroπ   10h     4      BX or EBXπ   14h     4      DX or EDXπ   18h     4      CX or ECXπ   1Ch     4      AX or EAXπ   20h     2      CPU status flagsπ   22h     2      ESπ   24h     2      DSπ   26h     2      FSπ   28h     2      GSπ   2Ah     2      IP (reserved, ignored)π   2Ch     2      CS (reserved, ignored)π   2Eh     2      SP (ignored when zero)π   30h     2      SS (ignored when zero)ππ   In the following example, I set the Registers For the above messageπ   exchanging Function. It's best to clear all Registers (or at leastπ   the SS:SP Registers) beFore calling the Simulate Real Mode Interrupt.ππ     Uses DPMI;π     Varπ       Regs : TRealModeRegs;π     beginπ       FillChar(Regs, Sizeof(TRealModeRegs), #0); {* clear all Registers *}ππ       With Regs do  beginπ         ah := $xx;π         es := PtrRec(RealModeSeg).Seg;π         di := PtrRec(RealModeSeg).ofsπ       end;  { of With }π     end;ππ   All this is fairly standard. Just set up the Registers you interruptsπ   expect, very much like the Intr Procedure.ππ4) We can now issue the interrupt in Real mode using the RealModeIntπ   Procedure (in the DPMI Unit). Its definition isππ     Procedure RealModeInt(Int: Byte; Var Regs: TRealModeRegs);ππ   or you can call int 31h, Function 0300h, see Ralph Brown's interruptπ   list.π   For our message exchanging Program it would simply be:π     RealModeInt(xx, Regs);ππ5) Examine the results. Modified Registers are passed in the Regs dataπ   structure so you can check the results.π       It is necessary to discriminate between to Types of returnedπ   segments. In the example above, I assumed that the Interrupt returnedπ   data in the allocated memory block. I already have a selector Forπ   that block, so I can examine the results.π   Another Type of interrupt returns Pointers to segments it hasπ   allocated itself. As we don't have a selector For that memory blockπ   we have to create one. We need the following Functions:π   - AllocSelectors, to allocate a selectorπ   - SetSelectorBase, to let it point to a physical addressπ   - SetSelectorLimit, to set the sizeπ   An example For this situation: Assume that a certain interruptπ   returns a Pointer to a memory area. This Pointer is in es:di.π   Register cs contains the size of that memorya rea. I show you how toπ   acces that segment.ππ     Uses DPMI;π     Varπ       Regs : TRealModeRegs;π       p : Pointer;π     beginπ     {* setup  Regs *}π     {* issue interrupt, returning es:di *}ππ     {* as we don't have a selector, create one *}π       PtrRec(p).Seg := AllocSelectors(1);π       PtrRec(p).ofs := 0;ππ     {* this selector points to no physical address and has size 0 *}π     {* so let the selector point to es:di *}π       SetSelectorBase(PtrRec(p).Seg, Regs.es*16+Regs.di);ππ     {* Forgive me! This was a joke. The last statement does not work   *}π     {* of course. Regs.es*16+Regs.di will in the best Cases ({$R+,Q+}) *}π     {* result in an overflow error. You have to Write:                 *}π       SetSelectorBase(PtrRec(p).Seg, Regs.es*LongInt(16)+Regs.di);ππ     {* the selector now points to a memory area of size 0 *}π       SetSelectorLimit(PtrRec(p).Seg, Regs.cx);ππ     {* we don't have to set the accesrights (code/data, read/Write, etc. *}π     {* as they are almost ok *}ππ     {* we can now acces this memory using selector p *}π     { <acces block> }ππ     {* after using it, free selector *}π       FreeSelector(PtrRec(p).Seg);π     end;πππAre there any questions? No? Let's go ahead than to the next Type ofπinterrupts.ππUnsupported interrupts which use the stackπ------------------------------------------πThe second Type of unsupported interrupts are the ones which make use ofπthe stack. We can distinguish between:π1. interrupts which need parameters on the stackπ2. interrupts which return parameters on the stackππ1) For the first Type we need to setup a stack. There is an extraπ   Compilication, which I had not told yet. As the stack in protectedπ   mode resides in a protected mode segment it is unusable For the Realπ   mode interrups. So Borland's DPMI Extender switches from theπ   protected to a Real mode stack (and back). We can supply a defaultπ   Real mode stack if we set the stack Registers (ss and sp) in the Realπ   mode register data structure to zero. else it is assumed that ss:spπ   points to a Real mode stack. Failure to set them up properly couldπ   have disastrous results!ππ   We will have to do:π   1) create a Real mode stack using GlobalDosAllocπ   2) fill this stack With valuesπ   3) set ss and sp properlyπ   4) issue interruptππ   All in one example Program. The following Program sets DesqView'sπ   mouse on a given location on the screen. The supplied handle is theπ   handle of the mouse. As DesqView needs dWord values on the stack Iπ   allocated a LongIntArray stack which is defined as:ππ     Constπ       MaxLongIntArray = 1000;π     Typeπ       PLongIntArray = ^TLongIntArray;π       TLongIntArray = Array [0..MaxLongIntArray] of LongInt;ππ   The example Program:ππ     Procedure SetMouse(Handle, x, y : LongInt);π     Constπ       StackSize = 3*Sizeof(LongInt);π     Varπ       Regs : TRealModeRegs;π       Stack : PLongIntArray;π       l : LongInt;π     beginπ     {* clear all Registers *}π       FillChar(Regs, Sizeof(TRealModeRegs), 0);ππ     {* setup the Registers *}π       Regs.ax := $1200;π       Regs.bx := $0500;ππ     {* allocate the stack *}π       l := GlobalDosAlloc(StackSize);ππ     {* set stacksegment register sp. ss should be set to the bottom of *}π     {* the stack = 0 *}π       Regs.sp := LongRec(l).Hi;π       Stack := Ptr(LongRec(l).Lo, 0);ππ     {* fill the stack *}π       Stack^[0] := Handle;π       Stack^[1] := y;π       Stack^[2] := x;ππ     {* issue the interrupt *}π       RealModeInt($15, Regs);ππ     {* free the stack *}π       GlobalDosFree(PtrRec(Stack).Seg);π     end;ππ2) Looks much like solution above. if only values are returned on theπ   stack. Don't Forget to set sp to the top of the stack. In the aboveπ   example settings Regs.sp := StackSize;π       An example is given below, where a solution to my originalπ   problem is given.πππSolution For the dv_win_me Procedure:ππ  Uses DVAPI, Objects, WinApi, WinTypes, DPMI;ππ  Function dv_win_me : LongInt;π  Constπ    StackSize = Sizeof(LongInt);π  Varπ    Regs : TRealModeRegs;π    RealStackSeg : Word;π    RealStackSel : Word;π    l : LongInt;π  beginπ  {* clear all Registers *}π    FillChar(Regs, Sizeof(TRealModeRegs), #0);ππ  {* allocate a 1 dWord stack *}π    l := GlobalDosAlloc(StackSize);π    RealStackSeg := LongRec(l).Hi;π    RealStackSel := LongRec(l).Lo;ππ  {* clear the stack (not necessary) *}π    FillChar(Ptr(RealStackSel, 0)^, StackSize, #0);ππ  {* set Registers *}π    With Regs do  beginπ      bx := $0001;π      ah := $12;π      ss := RealStackSeg;π      sp := StackSize;π    end;  { of With }ππ  {* perForm Real mode interrupt *}π    RealModeInt($15, Regs);π    dv_win_me := PLongInt(Ptr(RealStackSel, 0))^;ππ  {* free the stack *}π    GlobalDosFree(PtrRec(RealStackSel).Seg);π  end;ππ  beginπ    Writeln(dv_win_me);π  end.πππYou see, code size bloats in protected mode! (ThereFore Borland gave usπ16MB....)πππAppendix A.π-----------ππAs promised, some routines I found usefull when working With Real modeπsegments.ππ====================cut here====================πUnit DPMIUtil;ππInterfaceππUses Objects, DPMI;ππConstπ  MaxLongIntArray = 1000;πTypeπ{* this Type is usefull For DesqView stacks *}π  PLongIntArray = ^TLongIntArray;π  TLongIntArray = Array [0..MaxLongIntArray] of LongInt;πππ{* clear all Registers to zero *}ππProcedure ClearRegs(Var Regs : TRealModeRegs);ππ{* allocate memory using GlobalDosAlloc and split the returned *}π{* LongInt into a protected mode Pointer and a Real mode segment *}ππFunction  XGlobalDosAlloc(Size : LongInt; Var RealSeg : Word) : Pointer;ππ{* free memory *}ππProcedure XGlobalDosFree(p : Pointer);πππImplementationππUses WinAPI;ππProcedure ClearRegs(Var Regs : TRealModeRegs);πbeginπ  FillChar(Regs, Sizeof(TRealModeRegs), 0);πend;ππFunction  XGlobalDosAlloc(Size : LongInt; Var RealSeg : Word) : Pointer;πVarπ  l : LongInt;πbeginπ  l := GlobalDosAlloc(Size);π  RealSeg := LongRec(l).Hi;π  XGlobalDosAlloc := Ptr(LongRec(l).Lo, 0);πend;ππProcedure XGlobalDosFree(p : Pointer);πbeginπ  GlobalDosFree(PtrRec(p).Seg);πend;ππend.  { of Unit DPMIUtil }π====================cut here====================πππExample code how to use it. The above dv_win_me routine would look like:ππ  Uses DVAPI, Objects, WinApi, WinTypes, DPMI;ππ  Function dv_win_me : LongInt;π  Constπ    StackSize = Sizeof(LongInt);π  Varπ    Regs : TRealModeRegs;π    Stack : PLongIntArray;π  beginπ  {* clear all Registers *}π    ClearReges(Regs);ππ  {* allocate a 1 dWord stack *}π    Stack := XGlobalDosAlloc(StackSize, Regs.ss);ππ  {* set Registers *}π    Regs.bx := $0001;π    Regs.ah := $12;π    Regs.sp := StackSize;ππ  {* perForm Real mode interrupt *}π    RealModeInt($15, Regs);π    dv_win_me := Stack^[0];ππ  {* free the stack *}π    XGlobalDosFree(Stack);π  end;ππ  beginπ    Writeln(dv_win_me);π  end.ππCompare this to the previous code. It just looks a bit prettierπaccording to my honest opininion.πππConclusionπ----------ππAs you saw, the switch from Real to protected mode may be ratherπpainfull. I hope With the above examples and explanations you can makeπit a bit more enjoyable. One question remains: why did Borland notπclearly told us so? Why not present a few examples, warnings, etc.?πMaybe RiChard Nelson can answer this questions For us. Everything heπsays is his private opinion of course, but a look in the kitchen couldπbe worthWhile.ππif you still have questions, I'm willing to answer them in eitherπusenet's Comp.LANG.PASCAL or fidonet's PASCAL.028 or PASCAL. I can'tπport your library of course but if the inFormation presented here is notπenough, just ask.ππππReferencesπ----------π- The usual Borland set of handbooksππ- "Borland Open Architecture Handbook For Pascal", sold separately byπBorland,π  184 pages.ππ- "Extending Dos, a Programmer's Guide to protected-mode Dos", Rayπ  Duncan, Charles Petzold, andrew Schulman, M. Steven Baker, Ross P.π  Nelson, Stephen R. Davis and Robert Moote. Addison-Wesly, 1992.π  ISBN: 0-201-56798-9ππ- "PC Magazine Programmer's Technical Reference: The Processor andπ   Coprocessor", Robert L. Hummel. Ziff-Davis Press, 1992.π  ISBN: 1-56276-016-5πππ  { Dunno if this came before or after this message :) }ππHello Protectors,ππof course, a few hours after my message has been released to the net,πbugfixes seem necessary )-:ππSome minor bugfixes:ππ* In the example about allocating memory below the 1MB, memory isπ  allocated but not released. As we have only 1MB down their, this canπ  become a problem ;-)π  Fix:  adding the statementπ    GlobalDosFree(RealModeSel);π  will clean things upππ * The solution to interrupts which requires parameters passed on theπ  stack has a bug. Theπ    les  di,Regsπ  statement does not work of course. Replace byπ    mov  di,ofFSET Regsπ    mov  dx,SEG Regsπ    mov  es,dxπ  This does not work when Regs is declared in the stack segment (wellπ  done Borland....), you encounter bug number 16, just as I did.... (seeπ  next message)πππ                                                                    5      05-28-9313:50ALL                      SWAG SUPPORT TEAM        FASTMEM.PAS              IMPORT              36          Unit MEM16;π{π Author:        Paul VanderSpekπ Date:          03-20-1993ππ    This source code is being released as Free-Ware.  You may useπ  this code in your Programs and modify it to fit your needs. Theπ  only  restrictions are that you  may not distribute the  sourceπ  code in modified Form or Charge For the source code itself.πππ}ππInterfaceππProcedure Move16(Var Source,Dest;Count:Word);πProcedure FillChar16(Var X; Count: Word; Value:Byte);ππImplementationππProcedure Move16(Var Source,Dest;Count:Word); Assembler;πAsmπ  PUSH DSπ  LDS SI,SOURCEπ  LES DI,DESTπ  MOV AX,COUNTπ  MOV CX,AXπ  SHR CX,1π  REP MOVSWπ  TEST AX,1π  JZ @endπ  MOVSBπ@end:POP DSπend;ππProcedure FillChar16(Var X; Count: Word; Value:Byte); Assembler;πAsmπ  LES DI,Xπ  MOV CX,COUNTπ  SHR CX,1π  MOV AL,ValUEπ  MOV AH,ALπ  REP StoSWπ  TEST COUNT,1π  JZ @endπ  StoSBπ@end:πend;ππend.ππ{πThese routines are twice as fast as the normal Move and FillChar routinesπsInce they use MOVSW and StoSW instead of MOVSB and StoSB. They work inπexactly the same way, so you can just replace Move and FillChar With them.π}ππ{π>     This source code is being released as Free-Ware.  You may useπ>   this code in your Programs and modify it to fit your needs. Theπ>   only  restrictions are that you  may not distribute the  sourceπ>   code in modified form or Charge For the source code itself.ππI'm sorry to say that I'm not impressed, since hundreds of people already haveπinvented this wheel.  Besides, your move routine has at least one serious flaw:πit assumes that source and destinaton do not overlap.  Which is not always theπcase; if you have a Variable of the Type String as the source, and you want toπcopy a few Characters furtheron in this Variable, you'll mess up the result.ππ>      SHR CX,1π>      REP MOVSWπ>      TEST AX,1π>      JZ @endπ>      MOVSBπ> @end:POP DSππThe TEST AX, 1 instruction is superfluous.  If the number of Bytes in the CXπregister is odd, the SHR CX, 1 instruction will set the carry bit.  It's moreπconvenient to test this bit.  Here's how:ππ         SHR   CX, 1π         JNC   @1π         MOVSBπ         REP   MOVSWπ    @1:ππ> Have Fun,ππNo fun if source and destination overlap, as said earlier.  Here follows aπmemory move routine With 16-bit moves and overlap check:π}πProcedure MoveMem(Var source, target; size : Word); Assembler;ππAsmπ        PUSH    DSπ        LDS     SI, sourceπ        LES     DI, targetπ        MOV     CX, sizeπ        CLDππ    { If an overlap of source and target could occur,π      copy data backwards }ππ        CMP     SI, DIπ        JAE     @2ππ        ADD     SI, CXπ        ADD     DI, CXπ        DEC     SIπ        DEC     DIπ        STDππ        SHR     CX, 1π        JAE     @1π        MOVSBπ@1:     DEC     SIπ        DEC     DIπ        JMP     @3ππ@2:     SHR     CX, 1π        JNC     @3π        MOVSBππ@3:     REP     MOVSWπ        POP     DSπend;  { MoveMem }πππ{π> For I := 0 to 200 doπ>  Move(Buffer,Mem[$A000:0000],320);ππLooks weird to me. Why moving all that stuff 200 times to the first lineπof the screen ?ππ> For I := 100 to 200 doπ>  Move(Buffer[320*I],Mem[$A000:(I*320)],320);ππThis could be done viaππMove(Buffer[320*StartLine], Mem[$a000:320*StartLine], 320*NumberOfLines) ;ππwhich should somehow be faster.ππAlso note that TP's Move Procedure Uses a LODSB instruction, which isπtwice as slow as a LODSW instruction on 286+ computers, With big buffers.πSo here is a replacement Move proc, which works fine EXCEPT if the twoπbuffers overlap and destination is at a greater address than source, whichπanyway is not the Case here.π}πProcedure FastMove(Var Src, Dst ; Cnt : Word) ;πAssembler ;πAsmπ     Mov       DX, DS         { Sauvegarde DS }π     Mov       CX, Cntπ     LDS       SI, Srcπ     LES       DI, Dstπ     ClD                      { A priori, on va du dbut vers la fin }π     ShR       CX, 1          { On va travailler sur des mots }π     Rep       MovSW          { Copie des mots }π     JNC       @Done          { Pas d'octet restant (Cnt pair) ? }π     MovSB                    { Copie dernier octet }π@Done:π     Mov       DS, DX         { Restauration DS }πend ;π{πWell, just a note : this proc works twice faster than TP's Move _only_ ifπSrc and Dst are Word aligned, which is the Case if :π- they are Variables allocated on the heap,π- they are declared in the stack,π- $a+ is specified,π- you use it as described in your examples of code :-)π}                                                             6      05-28-9313:50ALL                      SWAG SUPPORT TEAM        FREEMEM1.PAS             IMPORT              4           {πDoes anyone have any routines to find the available memory outside of theπheap ?π}ππFunction GetFreeMemory : LongInt;πVarπ  Regs : Registers;πbeginπ  Regs.AH := $48;π  Regs.BX := $FFFF;π  Intr($21,Regs);π  GetFreeMemory := LongInt(Regs.BX)*16;πend;π{ππThis Procedure tries to allocate 1MB memory (what's impossible).πDos will give you the maximum of free memory back.π}             7      05-28-9313:50ALL                      SWAG SUPPORT TEAM        GETCACHE.PAS             IMPORT              34          {π>  Well is there a way to find out if Norton Cache is installed?ππTest For SmartDrv.* , HyperDsk only.    ! Others Untested !π}ππProgram IsThereAnyCache;πUsesπ  Dos;ππConstπ  AktCache   : Byte = 0;π  CacheNames : Array[0..10] of String[25] = (π     '*NO* Disk-Cache found','SmartDrv.Exe','SmartDrv.Sys',π     'Compaq SysPro','PC-Cache V6.x','PC-Cache V5.x',π     'HyperDsk ?', 'NCache-F','NCache-S',π     'IBMCache.Sys','Q-Cache (?)');ππVarπ  Version : Integer;π  Regs    : Registers;ππFunction SmartDrvVersion:Integer;πVarπ  Bytes : Array[0..$27] of Byte; { return Buffer }π  TFile : Text;πbeginπ  SmartDrvVersion := -1;  { assume NO smartdrv ! }π  {--------Check For SmartDrv.EXE---------- }π  FillChar( Regs, Sizeof(Regs), 0 );π  Regs.AX := $4A10;  { install-check }π  Intr( $2F, Regs );π  if Regs.FLAGS and FCARRY = 0 then  { OK! }π  beginπ    if Regs.AX = $BABE then          { the MAGIC-# }π    beginπ      SmartDrvVersion := Integer(Regs.BP);π      AktCache := 1;π      Exit;π    end;π  end;π  { -------Check For SmartDrv.SYS----------- }π  Assign(TFile,'SMARTAAR');π  {$I-}π  Reset(TFile);π  {$I+}π  if IOResult <> 0 thenπ    Exit; { No SmartDrv }π  FillChar( Regs, Sizeof(Regs), 0 );π  Regs.AX := $4402; { IoCtl }π  Regs.BX := TextRec(TFile).Handle;π  Regs.CX := Sizeof(Bytes);π  Regs.DS := Seg(Bytes);π  Regs.DX := Ofs(Bytes);π  MsDos(Regs); { int 21h }π  Close(TFile);π  if Regs.FLAGS and FCARRY <> 0 thenπ    Exit;  { Error-# in Regs.AX ...}π  SmartDrvVersion :=  Bytes[$E] + 256 * Bytes[$F];π  AktCache := 2;πend;ππFunction CompaqPro : Integer;πbeginπ  CompaqPro := -1;π  Regs.AX := $F400;π  Intr($16, Regs);π  if Regs.AH <> $E2 thenπ    Exit;π  if Regs.AL in[1,2] thenπ    AktCache := 3;π  CompaqPro := $100;πend;ππFunction PC6 : Integer;   { PCTools v6, v5 }πbeginπ  PC6 := -1;π  Regs.AX := $FFA5;π  Regs.CX := $1111;π  Intr($16, Regs);π  if Regs.CH <> 0 thenπ    Exit;π  PC6 := $600;π  AktCache := 4;πend;ππFunction PC5 : Integer;πbeginπ  PC5 := -1;π  Regs.AH := $2B;π  Regs.CX := $4358; {'CX'}π  Intr($21, Regs);π  if Regs.AL <> 0 thenπ    Exit;π  PC5 := $500;π  AktCache := 5;πend;ππFunction HyperDsk : Integer;   { 4.20+ ... }πbeginπ  Hyperdsk:= -1;π  Regs.AX := $DF00;π  Regs.BX := $4448; {'DH'}π  Intr($2F, Regs);π  if Regs.AL <> $FF   thenπ    Exit;π  if Regs.CX <> $5948 thenπ    Exit; { not a "Hyper" product }π  HyperDsk := Regs.DX;π  AktCache := 6;πend;ππFunction Norton : Integer;πbeginπ  Norton := -1;π  Regs.AX := $FE00;π  Regs.DI := $4E55; {'NU'}π  Regs.SI := $4353; {'CS' test For Ncache-S v5 }π  Intr($2F, Regs);π  if Regs.AH = $00 thenπ  beginπ    Norton := $500;π    AktCache := 7;π    Exit;π  end;π  { Test For Ncache-F v5 / v6 }π  Regs.AX := $FE00;π  Regs.DI := $4E55; {'NU'}π  Regs.SI := $4353; {'CF' test For Ncache-F v5, V6+ }π  Intr($2F, Regs);π  if Regs.AH <> $00 thenπ    Exit;π  Norton := $600;π  AktCache := 8;πend;ππFunction IBM : Integer;πbeginπ  IBM:= -1;π  Regs.AX := $1D01;π  Regs.Dl := $2; { drive C: }π  Intr($13, Regs);π  if Regs.Flags and FCarry <> 0 thenπ    Exit;π  { ES:(BX+$22) -> ASCII-Version-# }π  Inc( Regs.BX, $22 );π  Regs.AH := (Mem[Regs.ES : Regs.BX] - $30 ) shl 4;π  Regs.AH := Regs.AH or (Mem[Regs.ES : Regs.BX + 1] - $30 );π  Regs.AL := (Mem[Regs.ES : Regs.BX + 2] - $30 ) shl 4;π  Regs.AL := Regs.AL or (Mem[Regs.ES : Regs.BX + 3] - $30 );π  IBM := Regs.AX;π  AktCache := 9;πend;ππFunction QCache : Integer;πbeginπ  QCache := -1;π  Regs.AH := $27;π  Regs.BX := 0;π  intr($13,Regs);π  if Regs.BX = 0 thenπ    Exit;π  QCache := Regs.BX;  { ??? }π  AktCache := 10;πend;ππbeginπ  Writeln('DISK-CACHE-CHECK v1.00    Norbert Igl ''1/93');π  Version := SmartDrvVersion;π  if Aktcache = 0 thenπ    Version := Hyperdsk;π  if Aktcache = 0 thenπ    Version := Norton;π  if Aktcache = 0 thenπ    Version := PC6;π  if Aktcache = 0 thenπ    Version := PC5;π  if Aktcache = 0 thenπ    Version := IBM;π  if Aktcache = 0 thenπ    Version := QCache;π  if Aktcache = 0 thenπ    Version := CompaqPro;ππ  Write(CacheNames[AktCache]);π  if AktCache <> 0 thenπ    Writeln(' (V', Version div 256, '.', Version mod 256, ') installed.');π  Writeln;πend.π                                                                                                                             8      05-28-9313:50ALL                      SWAG SUPPORT TEAM        MALLOC.PAS               IMPORT              28          {$S-,R-,V-,I-,N-,B-,F-}ππ{$IFNDEF Ver40}π{Allow overlays}π{$F+,O-,X+,A-}π{$ENDIF}ππUNIT MemAlloc;ππ  { Purpose is to provide the ability to create (destroy) dynamic variables  }π  { without needing to reserve heap space at compile time.                   }ππINTERFACEππFUNCTION Malloc(VAR Ptr; Size : Word) : Word;π  { Allocate free memory and return a pointer to it.  The amount of memory      }π  { requested from DOS is calculated as (Size/4)+1 paragraphs.  If the          }π  { allocation is successful, the untyped VAR parameter Ptr will be populated   }π  { with the address of the allocated memory block, and the function will return}π  { a zero result.  Should the request to DOS fail, Ptr will be populated with  }π  { the value NIL, and the function will return the appropriate DOS error code. }ππFUNCTION Dalloc(VAR Ptr) : Word;π  { Deallocate the memory pointed to by the untyped VAR parameter Ptr           }ππIMPLEMENTATIONππ  FUNCTION Malloc(VAR Ptr; Size : Word) : Word;π  BEGINπ    INLINE(π      $8B / $46 / <Size /         {            mov         ax,[bp+<Size]}π      $B9 / $04 / $00 /           {            mov         cx,4}π      $D3 / $E8 /                 {            shr         ax,cl}π      $40 /                       {            inc         ax}π      $89 / $C3 /                 {            mov         bx,ax}π      $B4 / $48 /                 {            mov         ah,$48}π      $CD / $21 /                 {            int         $21             ;Allocate memory}π      $72 / $07 /                 {            jc          AllocErr        ;If any errors ....}π      $C7 / $46 / $FE / $00 / $00 / {NoErrors:   mov word    [bp-2],0        ;Return 0 for successful allocation}π      $EB / $05 /                 {            jmp short   Exit}π      $89 / $46 / $FE /           {AllocErr:   mov         [bp-2],ax       ;Return error code}π      $31 / $C0 /                 {            xor         ax,ax           ;Store a NIL value into the ptr}π      $C4 / $7E / <Ptr /          {Exit:       les         di,[bp+<Ptr]    ;Address of pointer into es:di}π      $50 /                       {            push        ax              ;Save the Segment part}π      $31 / $C0 /                 {            xor         ax,ax           ;Offset is always 0}π      $FC /                       {            cld                         ;Make sure direction is upward}π      $AB /                       {            stosw                       ;Store offset of memory block}π      $58 /                       {            pop         ax              ;Get back segment part}π      $AB);                       {            stosw                       ;Store segment of memory block}π    π  END {Malloc} ;ππ  FUNCTION Dalloc(VAR Ptr) : Word;π  BEGINπ    IF Pointer(Ptr) <> NIL THEN BEGINπ      INLINE(π        $B4 / $49 /               {            mov         ah,$49}π        $C4 / $7E / <Ptr /        {            les         di,[bp+<Ptr]}π        $26 / $C4 / $3D /         {        es: les         di,[di]}π        $CD / $21 /               {            int         $21}π        $72 / $02 /               {            jc          Exit}π        $31 / $C0 /               {NoError:    xor         ax,ax}π        $89 / $46 / $FE);         {Exit:       mov         [bp-2],ax}π      Pointer(Ptr) := NIL;π    END {if} ;π  END {Dealloc} ;ππEND {Unit MemAlloc} .ππ                                                                                9      05-28-9313:50ALL                      SWAG SUPPORT TEAM        MEMALLOC.PAS             IMPORT              37          {This is a Unit MEMALLOC.PAS For use With the .VOC player...}πUnit MemAlloc;ππ{ Purpose is to provide the ability to create (destroy) dynamic Variables  }π{ without needing to reserve heap space at Compile time.                   }ππInterfaceππFunction Malloc(Var Ptr; Size : Word) : Word;π{ Allocate free memory and return a Pointer to it.  The amount of memoryπ{ requested from Dos is calculated as (Size/4)+1 paraGraphs.  if theπ{ allocation is successful, the unTyped Var parameter Ptr will be populatedπ{ With the address of the allocated memory block, and the Function will return}π{ a zero result.  Should the request to Dos fail, Ptr will be populated withπ{ the value NIL, and the Function will return the appropriate Dos error code.π}ππFunction Dalloc(Var Ptr) : Word;π{ Deallocate the memory pointed to by the unTyped Var parameter Ptrπ}ππFunction DosMemAvail : LongInt;π{ Return the size of the largest contiguous chuck of memory available For useπ}ππ{ ---------------------------------------------------------------------------π}ππImplementationππ{ ---------------------------------------------------------------------------π}ππFunction Malloc(Var Ptr; Size : Word) : Word;πbeginπ   Inline(π     $8B/$46/<SIZE/         {            mov         ax,[bp+<Size]}π     $B9/$04/$00/           {            mov         cx,4}π     $D3/$E8/               {            shr         ax,cl}π     $40/                   {            inc         ax}π     $89/$C3/               {            mov         bx,ax}π     $B4/$48/               {            mov         ah,$48}π     $CD/$21/               {            int         $21             ;Allocate memory}π     $72/$07/               {            jc          AllocErr        ;if any errors ....}π     $C7/$46/$FE/$00/$00/   {NoErrors:   mov Word    [bp-2],0        ;Return 0 For successful allocation}π     $EB/$05/               {            jmp short   Exit}π     $89/$46/$FE/           {AllocErr:   mov         [bp-2],ax       ;Return error code}π     $31/$C0/               {            xor         ax,ax           ;Store a NIL value into the ptr}π     $C4/$7E/<PTR/          {Exit:       les         di,[bp+<Ptr]    ;Address of Pointer into es:di}π     $50/                   {            push        ax              ;Save the Segment part}π     $31/$C0/               {            xor         ax,ax           ;offset is always 0}π     $FC/                   {            cld                         ;Make sure direction is upward}π     $AB/                   {            stosw                       ;Store offset of memory block}π     $58/                   {            pop         ax              ;Get back segment part}π     $AB);                  {            stosw                       ;Store segment of memory block}ππend {Malloc};ππ{ ---------------------------------------------------------------------------π}ππFunction Dalloc(Var Ptr) : Word;πbeginπ   if Pointer(Ptr) <> NIL then beginπ      Inline(π        $B4/$49/               {            mov         ah,$49}π        $C4/$7E/<PTR/          {            les         di,[bp+<Ptr]}π        $26/$C4/$3D/           {        es: les         di,[di]}π        $CD/$21/               {            int         $21}π        $72/$02/               {            jc          Exit}π        $31/$C0/               {NoError:    xor         ax,ax}π        $89/$46/$FE);          {Exit:       mov         [bp-2],ax}π      Pointer(Ptr) := NIL;π   end {if}π   elseπ      Dalloc := 0;πend {Dealloc};ππ{ ---------------------------------------------------------------------------π}ππFunction DosMemAvail : LongInt;πbeginπ   Inline(π     $BB/$FF/$FF/           {         mov         bx,$FFFF}π     $B4/$48/               {         mov         ah,$48}π     $CD/$21/               {         int         $21}π     $89/$D8/               {         mov         ax,bx}π     $B9/$10/$00/           {         mov         cx,16}π     $F7/$E1/               {         mul         cx}π     $89/$46/$FC/           {         mov         [bp-4],ax}π     $89/$56/$FE);          {         mov         [bp-2],dx}πend; {DosMemAvail}ππend. {Unit MemAlloc}ππ{Ok.. The Code can be rewritten to use GetMem and FreeMem (in fact I suggestπyou do this). I rewrote it myself to do so, but this is the distribution copy.π(I made one change in line 316-318 of SBVOICE.PAS bumping up the driver sizeπfrom 3000 to 5000 to accomodate the SoundBlaster 2.0 driver)πThis Program requires CT-VOICE.DRV which is distributed With the Soundblaster.π}π                                                                                                10     05-28-9313:50ALL                      SWAG SUPPORT TEAM        MEMINFO.PAS              IMPORT              32          {πFirst of all something about Turbo Pascal memory management. A Turbo PascalπProgram Uses the upper part of the memory block it allocates as the heap.πThe heap is the memory allocated when using the Procedures 'New' andπ'GetMem'. The heap starts at the address location pointed to by 'Heaporg' andπgrows to higher addresses as more memory is allocated. The top of the heap,πthe first address of allocatable memory space above the allocated memoryπspace, is pointed to by 'HeapPtr'.ππMemory is deallocated by the Procedures 'Dispose' and 'FreeMem'. As memoryπblocks are deallocated more memory becomes available, but..... When a blockπof memory, which is not the top-most block in the heap is deallocated, a gapπin the heap will appear. to keep track of these gaps Turbo Pascal maintainsπa so called free list.ππThe Function 'MaxAvail' holds the size of the largest contiguous free blockπ_in_ the heap. The Function 'MemAvail' holds the sum of all free blocks inπthe heap.ππThus Far nothing has changed from TP5.5 to TP6.0. But here come theπdifferences:ππTP5.5ππto keep track of the free blocks in the heap, TP5.5 maintains a free listπwhich grows _down_ from the top of the heap. As more free blocks becomeπavailable, this list will grow. Every item in this list, a free-list Record,πcontains two four-Byte Pointers to the top and the bottom of a free blockπin the heap. _FreePtr_ points to the first free-list Record (the bottom mostπfree-list Record).ππThe minimum _allowable_ distance between 'FreePtr' and 'HeapPtr' can be setπwith the Variable 'FreeMin'.ππTP6.0ππIn TP6.0 the Variables 'FreePtr' and 'FreeMin' no longer exist. The free listπas implemented in TP5.5 no longer exists either (although the TP6.0πProgrammer's guide still mentions a down growing free list??)). TP6.0 keepsπtrack of the free blocks by writing a 'free list Record' to the first eightπBytes of the freed memory block! A (TP6.0) free-list Record contains two fourπByte Pointers of which the first one points to the next free memory block, theπsecond Pointer is not a Real Pointer but contains the size of the memory block.πSummaryππSo instead of a list of 'free list Records', growing down from the top of theπheap, containing Pointers to individual memory blocks, TP6.0 maintains a linkedπlist With block sizes and Pointers to the _next_ free block.πIn TP6.0 an extra heap Variable 'Heapend' designating the end of the heap isπadded. When 'HeapPtr' and 'FreeList' have the same value, the free list isπempty.ππThe below figure pictures the memory organization of both TP5.5 and TP6.0:πππ  TP5.5              TP6.0     Heapendπ───          ┌─────────┐                 ┌─────────┐ <────π ^    ┌──────│         │                 │         │π │    │      ├─────────┤                 │         │π │    │  ┌── │         │  FreePtr        │         │π │    │  │   ├─────────┤ <────           │         │πHeap  │  │   │         │                 │         │π │    │  │   │         │                 │         │π │    │  │   │         │                 │         │π v    │  │   │         │  HeapPtr        │         │  HeapPtrπ───   │  │   ├─────────┤ <────        ┌─>├─────────┤ <────π      │  │   │         │              │  │         │π      │  ├──>├─────────┤              │  ├─────────┤π      │  │   │  Free   │              └──│  Free   │π      │  └──>├─────────┤              ┌─>├─────────┤π      │      │         │              │  │         │π      ├─────>├─────────┤              │  ├─────────┤π      │      │  Free   │  Heaporg     └──│  Free   │  FreeListπ      └─────>├─────────┤ <────           ├─────────┤ <────π                                         │         │  Heaporgπ                                      ├─────────┤ <────ππππππI hope this will help you modifying existing toolBox's which make use of theseπdisappeared Variables. In some Case a modification may be quite easy, but asπyou see it might get quite quite difficult as well.π                                     11     05-28-9313:50ALL                      SWAG SUPPORT TEAM        MEMINFO2.PAS             IMPORT              10          π I need the proper syntax For a Pascal Program that will execute a Dosπ prog (a small one) and then resume the Pascal Program when the Dos progπ is finished.  Any suggestions gladly accepted...ππ   TP method:ππ   Assumes Programe name is \PROGPATH\PROGNAME.EXE, and the commandπ   line parameters are /Rππ   Exec('\PROGPATH\PROGNAME.EXE','/R');ππ   You need to make sure that you have the Heap set With the $Mπ   directives, so that you have enough memory to execute theπ   porgram.ππ   example (this Program doesn't use the heap at all):ππ   {$M 1024, 0, 0} { 1 kb stack, 0k min, 0k max }ππ   (this Program needs 20k minimum heap to run, and can use up toπ   100k)ππ   {$M 1024, 20480, 102400}  { 1k stack, 20k min, 100k max }ππ   A Turbo Pascal Program will always use as much RAM as there isπ   avaiable, up to the "max" limit. if you do not put a $M directiveπ   in your Program, the heap will be the entire available memory ofπ   your machine, so no memory will be available For your externalπ   Program to run.ππ   It is also a good idea to bracket your Exec command withπ   "SwapVector;" statements.π                                            12     05-28-9313:50ALL                      SWAG SUPPORT TEAM        OVERSIZE.PAS             IMPORT              45          Unit Oversize;ππ{  Author:  Trevor J Carlsenπ            Algorithm Enterprises Pty Ltdπ            PO Box 568π            Port Hedland  6721π            Western Australiaπ            Telephone:  (Voice)  +61 [0]91 73 2026π                        (Data )  +61 [0]91 73 2569π                        π  Released into the Public Domain 1991.ππ  An Unit that will enable logical Arrays to be created using up to the amount π  of heap memory available.ππ  The line marked (**) MUST be altered to reflect the Type of data in big π  Array and the Unit MUST be reCompiled after this change.ππ  No provision is made in this Unit For recovering the memory used by the big π  Array as the intention was to keep it appearing to the Programmer as close π  as possible to static Type global Variables.ππ  Bear in mind that you do not declare your Array anywhere using this Unit.  π  That is all handled automatically.  All you have to do is give the global π  Variable MaxElements a value With the number of elements you want in the π  Array and then call the Procedure initialise.  From then on your Array is π  called data^. (Actually it is called nothing as it is dynamic and is π  referenced via the Pointer "data" but if you think of it as being called π  "data^" you don't even need to know how Pointers work!)ππ  The Array, using this Unit, can only be singly dimensioned although there is π  no reason why the Unit could not be hacked to allow multi-dimensions.π  π }ππInterfaceππTypeπ(**)  DataType = LongInt;   { change to whatever Type you want For the Array }π  bigArray = Array[0..0] of DataType;π  bigptr   = ^bigArray;πVarπ  data : bigptr;π  MaxElements : LongInt;    { this Variable must have the number of elements }ππ{----------------------------------------------------------------------------}πFunction Element(l:LongInt):Byte;π  π  { Call by passing the element number you wish to reference.                }π  { Always returns zero.  It works by changing the value of the Pointer      }π  { data.  This means that you cannot ever reference your big Array by       }π  {   data^[100000] := whatever;                                             }π  { It MUST always be referenced by calling this Function eg.                }π  {   data^[Element(100000)] := whatever;                                    }π ππ{----------------------------------------------------------------------------}πFunction AllocateMem(Var b,l): Boolean;π  π  { Returns True if memory was allocated successfully For the big Array and  }π  { False if there was insufficient memory.                                  }ππ{----------------------------------------------------------------------------}πProcedure Initialise;  { Must be called beFore using any other Procedure     }ππ{============================================================================}ππImplementationππ{============================================================================}π{ private declarations }ππConstπ  max          = 65520 div sizeof(dataType);{ The number of elements/segment }π  initialised  : Boolean = False;π  πTypeπ  address  =  Record                     { allows arithmetic on the Pointers }π                offset,π                segment : Word;π              end;π  baseArray = Array[0..9] of address;    { For the addresses of the segments }ππVarπ  base : baseArray;π  ππ{----------------------------------------------------------------------------}πFunction Element(l:LongInt):Byte;ππ  Varπ    theaddress : address Absolute data;π    bigaddress : baseArray Absolute base;ππ  beginπ    π    { First make sure that initialisation has been done correctly            }π    if not initialised then begin π      Writeln('Initialise Procedure has not been called');π      halt(254);π    end;  { if not initialised }π    π    Element := 0; { It is Really irrelevent but any other value here would   }π                  { produce a range check error at runtime if R+             }π    π    { Now let us fool TP into thinking that the address of element zero is   }π    { address of the element we are looking For.                             }π    With theaddress do beginπ      segment := bigaddress[l div max].segment;            { Get the segment }π      offset  := (l mod max) * sizeof(dataType);            { Get the offset }π    end;  { With theaddress }π  end;  { ElementNumber }ππ{----------------------------------------------------------------------------}πFunction AllocateMem(Var b,l): Boolean;π  π  Typeπ    ptrArray = Array[0..9] of Pointer;π  Varπ    bArray: ptrArray Absolute b;π    x     : Byte;π    count : LongInt;π  beginπ    count := MaxElements;π    AllocateMem := True;π    For x := 0 to (count div max) do     { allocate in 64K contiguous chunks }π      if (count * sizeof(dataType)) > 65520 then beginπ        if MaxAvail < (max * sizeof(dataType)) then begin { not enough memory} π          dec(count,max);π          AllocateMem := False;π        end π        elseπ          GetMem(bArray[x],max * sizeof(dataType));π      endπ      elseπ        if MaxAvail < (count * sizeof(dataType)) thenπ           AllocateMem := Falseπ        elseπ           GetMem(bArray[x],count * sizeof(dataType)); π  end;  { AllocateMem }π  π{----------------------------------------------------------------------------}πProcedure Initialise;π  beginπ    FillChar(base,sizeof(base),0);π    if not AllocateMem(base,MaxElements) then beginπ      Writeln('Insufficient memory');π      halt(255);π    end;π    initialised := True;              { All Ok and memory has been allocated }π  end;  { Initialise }π  πend.  { Unit Oversize }ππ                               13     05-28-9313:50ALL                      SWAG SUPPORT TEAM        SDRVFLSH.PAS             IMPORT              7           {πHAGEN LEHMANNππThis Procedure flushes the SMARTDRV.EXE-cache.π}ππProcedure FlushChache; Assembler;πAsmπ  mov   ax,$4A10π  mov   bx,$0002π  int   $2Fπend;ππ{πMARCO MILTENBURGππFlushing SmartDrive: It's written by Max Maischein (2:249/6.17) and NorbertπIgl (2:2402/300.3), both from Germany (if I'm not mistaken).π}ππProcedure FlushSD_sys; Far;πVarπ  F : File;π  B : Byte;πbeginπ  Assign(F, 'SMARTAAR');π  Reset(F);π  B := 0;π  Asmπ    push  dsπ    mov   ax, 04403hπ    mov   bx, FileRec(F).Handleπ    mov   cx, 1π    int   21hπ    pop   dsπ  end;πend;ππProcedure FlushSD_exe; Far;πbeginπ  Asmπ    mov   ax, 04A10hπ    mov   bx, 1π    int   2Fhπ  end;πend;π                                                                                                               14     05-28-9313:50ALL                      SWAG SUPPORT TEAM        SMARTDRV.PAS             IMPORT              12          { MN> How can I find out if Smartdrv is installed ? I have  made a  harddiskπ MN> benchmark  Program,  and  I  would like  it to  detect if  Smartdrv isπ MN> installed.π}πUses Dos;ππFunction SmartDrvVersion:Integer;  { -1 means not inSTALLED }πVarπ  R: Registers;π  B: Array[0..$27] of Byte; { return Buffer }π  F: Text;ππbeginπ  SmartDrvVersion := -1;  { assume NO smartdrv ! }ππ  {--------Check For SmartDrv.EXE---------- }π  FillChar( R, Sizeof(R), 0 );π  R.AX := $4A10;  { install-check }π  Intr( $2F, R );π  if R.FLAGS and FCARRY = 0 then  { OK! }π    beginπ    if R.AX = $BABE then          { the MAGIC-# }π      beginπ        SmartDrvVersion := Integer(R.BP);π        Exitπ      end;π    end;π  { -------Check For SmartDrv.SYS----------- }π  Assign(f,'SMARTAAR');π  {$I-}π  Reset(f);π  {$I+}π  if IoResult <> 0 then Exit; { No SmartDrv }π  FillChar( R, Sizeof(R), 0 );π  R.AX := $4402; { IoCtl }π  R.BX := TextRec(f).Handle;π  R.CX := Sizeof(B);π  R.DS := Seg(B);π  R.DX := ofs(B);π  MsDos(R);  { int 21h }π  close(f);π  if R.FLAGS and FCARRY <> 0 then Exit;  { Error-# in R.AX ...}π  SmartDrvVersion :=  B[$E] + 256* B[$F];πend;ππVarπ  SMV:Integer;πbeginπ  SMV := SmartDrvVersion;π  Write(' SmartDrv');π  if SMV = -1 thenπ    Writeln('  not installed.')π  elseπ    Writeln('  V', SMV div 256,'.',SMV mod 256,' installed.');πend.π